package com.dhj.service.mongo.impl;

import com.alibaba.fastjson.JSONObject;
import com.dhj.entity.KeyTypeEntity;
import com.dhj.service.mongo.ICollectionService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;

import static com.dhj.constants.ConfigConstants.SUB_ELE_PREFIX;

/**
 * @Author gmd
 * @Description 集合信息service实现层
 * @Date 2023-08-04 18:39:54
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CollectionServiceImpl implements ICollectionService {

    private final MongoTemplate mongoTemplate;

    @Override
    public List<Map<String, String>> queryAllCollectName() {
        Set<String> collectionNames = mongoTemplate.getCollectionNames();
        List<Map<String, String>> result = new ArrayList<>();
        collectionNames.forEach(ele -> {
            Map<String, String> collection = new HashMap<>(4);
            collection.put("tableCommit", ele);
            collection.put("tableName", ele);

            result.add(collection);
        });
        return result;
    }

    @Override
    public List<Map<String, String>> queryCollectInfoByName(String collectName) {
        List<Map<String, String>> result = new ArrayList<>();
        JSONObject data = mongoTemplate.findOne(new Query(), JSONObject.class, collectName);
        if(Objects.isNull(data) || data.isEmpty()){
            log.error("{}表数据为空，对于MongoDB来说，无法获取其字段，请至少补充一条数据。", collectName);
            throw new RuntimeException(collectName+" 表数据为空");
        }
        int serialNo = 1;
        List<KeyTypeEntity> keysList = getKeyAndTypeOnErgodic(data, "");
        for (KeyTypeEntity ele : keysList) {
            result.add(getDefaultMap(ele.getFieldName(), ele.getTypeName(), serialNo++));
        }
        return result;
    }

    /**
     * 遍历元素的所有字段，以获取所有字段及字段类型信息，包括子元素（递归）
     *
     * @param ele    元素
     * @param prefix 字段名称前缀，主要是为了标识部分字段的父子关系
     */
    private List<KeyTypeEntity> getKeyAndTypeOnErgodic(JSONObject ele, String prefix) {
        List<KeyTypeEntity> keysList = new ArrayList<>();
        for (String key : ele.keySet()) {
            String typeName = ele.get(key).getClass().getSimpleName();
            // 如果是主键则插入到第一位，其他则按顺序插入
            if ("_id".equals(key) || "id".equals(key)) {
                keysList.add(0, new KeyTypeEntity(prefix + key, typeName));
            } else {
                keysList.add(new KeyTypeEntity(prefix + key, typeName));
            }

            // 如果字段数据类型是LinkedHashMap，则需要对其进行递归遍历
            if ("LinkedHashMap".equals(typeName)) {
                List<KeyTypeEntity> subKeys = getKeyAndTypeOnErgodic(ele.getJSONObject(key), SUB_ELE_PREFIX);
                keysList.addAll(subKeys);
            }
        }
        return keysList;
    }

    /**
     * 生成word的表单基本信息Map
     *
     * @param fieldName 字段名称
     * @param typeName  字段类型名称
     * @param serialNo  序列号
     */
    private Map<String, String> getDefaultMap(String fieldName, String typeName, Integer serialNo) {
        Map<String, String> ele = new HashMap<>(24);
        // 序号
        ele.put("serialNo", serialNo.toString());
        // 字段名
        ele.put("fieldName", fieldName);
        // 类型
        ele.put("type", typeName);
        // 长度
        ele.put("length", getTypeLength(typeName));
        // 类型(长度)
        ele.put("typeAndLen", typeName + "(" + getTypeLength(typeName) + ")");
        // 精度
        ele.put("precision", getTypePrecision(typeName));
        // 小数位数
        ele.put("numScale", getNumScale(typeName));
        // 允许为空
        ele.put("allowEmpty", getAllowEmpty(typeName));
        // 主键
        ele.put("mainKey", getMainKey(fieldName));
        // 默认值
        ele.put("defaultVal", getDefaultVal(typeName));
        // 描述
        ele.put("describeInfo", getDescribeInfo(fieldName));
        // 是否自增
        ele.put("increment", getIncrement(fieldName));

        return ele;
    }


    /**
     * 根据字段数据类型获取字段默认长度
     */
    private String getTypeLength(String typeName) {
        switch (typeName) {
            case "boolean":
                return "1";
            case "double":
                return "64";
            case "integer":
                return "11";
            case "long":
                return "20";
            case "objectId":
                return "32";
            default:
                return "";
        }
    }

    /**
     * 根据字段数据类型获取字段默认精度
     */
    private String getTypePrecision(String typeName) {
        switch (typeName) {
            case "boolean":
                return "1";
            case "double":
                return "64";
            case "integer":
                return "11";
            case "long":
                return "20";
            case "objectId":
                return "32";
            default:
                return "";
        }
    }

    /**
     * 根据字段数据类型获取字段允许的小数位
     */
    private String getNumScale(String typeName) {
        return "double".equals(typeName) ? "52" : "0";
    }

    /**
     * 根据字段数据类型获取字段是否允许为空
     */
    private String getAllowEmpty(String typeName) {
        switch (typeName) {
            case "boolean":
            case "date":
            case "objectId":
                return "NO";
            default:
                return "YES";
        }
    }

    /**
     * 根据字段名称判断是否是主键
     */
    private String getMainKey(String fieldName) {
        return ("_id".equals(fieldName) || "id".equals(fieldName)) ? "YES" : "NO";
    }

    /**
     * 根据字段数据类型获取字段的默认值
     */
    private String getDefaultVal(String typeName) {
        if (typeName.equals("boolean")) {
            return "false";
        }
        return "";
    }

    /**
     * 根据字段名称获取字段描述
     */
    private String getDescribeInfo(String fieldName) {
        return ("_id".equals(fieldName) || "id".equals(fieldName)) ? "唯一标识" : "";
    }

    /**
     * 根据字段名称判断是否只增（自生成）
     */
    private String getIncrement(String fieldName) {
        return ("_id".equals(fieldName) || "id".equals(fieldName)) ? "YES" : "NO";
    }

}
